package com.youshang.nio;

import com.google.common.base.Throwables;
import com.google.common.collect.Maps;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * @author youshang
 * @date 2021/09/12 17:05
 **/
public class NioServer {
    private static Map<String,SocketChannel> clientMap = Maps.newHashMap();

    public static void main(String[] args) throws Exception{

        Selector selector = init();
        //设置一个死循环，用于不停的处理接收到的事件
        while (true){
            //1. 调用selector 的 select 方法进行阻塞，一直等待直到SelectionKey 有时间发生的时候，select()方法就会返回selector所关注的事件集合
            //2. 通过selector获取到当前的SelectionKey集合
            //3. 获取到每一个selectionKey的 事件，针对每一个事件进行相对性的处理
                //3.1 当获取到连接事件时触发
                    //3.1.1 获取SelectionKey 的selectionKeys.isAcceptable() 事件，当连接建立成功之后会触发这个事件
                    //3.1.2 根据当前selectionKey 获取到 ServerSocketChannel对象(上一步注册的是什么 channel 这一步就可以强转为什么channel)
                    //3.1.4 获取当前连接的Socket Channel
                    //3.1.3 将当前socket Channel 设置为非堵塞的
                    //3.1.4 将Socket channel注册到selector，并且注册为selectionKey.OP_READ
                //3.2 当获取到读事件时触发
                    //3.2.1 通过selectionKey获取到SocketChannel()
                    //3.2.2 将当前Channel 中的数据读取到Buffer中
                    //3.2.3 操作buffer

            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            for (SelectionKey selectionKey : selectionKeys) {
                SocketChannel socketChannel = null;
                if (selectionKey.isAcceptable()) {
                    try {
                        ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                        socketChannel = channel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    } catch (IOException e) {
                        System.out.println(Throwables.getStackTraceAsString(e));
                    }
                    String key = "[" + UUID.randomUUID().toString()+ "]";
                    clientMap.put(key,socketChannel);
                } else if (selectionKey.isReadable()) {
                    String sendKey = null;
                    try {
                        socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        if (socketChannel.read(readBuffer) < 0) {
                            clientMap.put(sendKey,null);
                            selectionKey.cancel();
                            socketChannel.close();
                        }

                        readBuffer.flip();

                        Charset charset = Charset.forName("UTF-8");
                        String receivedMessage = String.valueOf(charset.decode(readBuffer).array());
                        System.out.println(socketChannel+" :  " + receivedMessage);


                        for (Map.Entry<String, SocketChannel> channelEntry : clientMap.entrySet()) {
                            if (socketChannel == channelEntry.getValue()) {
                                sendKey = channelEntry.getKey();
                                break;
                            }
                        }
                        for (Map.Entry<String, SocketChannel> channelEntry : clientMap.entrySet()) {
                            if (null == channelEntry || null == channelEntry.getValue()) {
                                continue;
                            }
                            socketChannel = channelEntry.getValue();
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            buffer.put((sendKey +" : "+ receivedMessage).getBytes());
                            buffer.flip();
                            socketChannel.write(buffer);
                        }
                    } catch (Exception e) {
                        System.out.println(Throwables.getStackTraceAsString(e));
                        clientMap.put(sendKey,null);
                        selectionKey.cancel();
                        socketChannel.close();
                    }
                }
            }
            selectionKeys.clear();
        }
    }
    public static Selector init() throws Exception{
        //1.先获取ServerSocketChannel
        //2. 设置为非堵塞的
        //3. 获取ServerSocket
        //4. 将当前socket绑定到指定端口
        //5. 获取Selector
        //6. 将当前的channel注册到selector，并且设置为OP_ACCECEPT

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress("localhost",8899));

        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        return selector;
    }
}
